package com.college.common.utils.security;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.college.common.utils.json.JsonSortUtil;
import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * RSA加解密
 * <p>
 * 字符串格式的密钥在未在特殊说明情况下都为BASE64编码格式<br/>
 * 由于非对称加密速度极其缓慢，一般文件不使用它来加密而是使用对称加密，<br/>
 * 非对称加密算法可以用来对对称加密的密钥加密，这样保证密钥的安全也就保证了数据的安全
 * </P>
 *
 * @author kuangxiang
 * @date 2021/05/19 15:31
 */
@Slf4j
public class RSAUtil {


    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";
    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    /**
     * 获取公钥的key
     */
    private static final String PUBLIC_KEY = "RSAPublicKey";
    /**
     * 获取私钥的key
     */
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /**
     * 生成数字签名sign
     *
     * @param data       待加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static String sign(String data, String privateKey) throws Exception {
        byte[] keyBytes = decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data.getBytes());
        return encode(signature.sign());
    }

    /**
     * 验签
     *
     * @param data      已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @param sign      数字签名
     * @return
     * @throws Exception
     */
    public static boolean verify(String data, String publicKey, String sign) throws Exception {
        byte[] keyBytes = decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data.getBytes());
        return signature.verify(decode(sign));
    }

    /**
     * 获取私钥
     *
     * @param keyMap 密钥对
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return encode(key.getEncoded());
    }

    /**
     * 获取公钥
     *
     * @param keyMap 密钥对
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return encode(key.getEncoded());
    }

    /**
     * 生成密钥对(公钥和私钥)
     *
     * @return
     * @throws Exception
     */
    public static Map<String, Object> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(2048);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * 按key进行正序排列，之间以&相连
     * <功能描述>
     *
     * @param params
     * @return
     */
    public static String getSortParams(Map<String, Object> params) {
        Map<String, Object> map = new TreeMap<>(Comparator.naturalOrder());
        map.putAll(params);

        StringBuffer stringBuffer = new StringBuffer();
        map.forEach((k, v) -> stringBuffer.append(k).append("=").append(v).append("&"));

        String str = stringBuffer.toString();

        if (str.length() > 0) {
            str = str.substring(0, str.length() - 1);
        }
        return str;
    }

    public static String getSortParams(String data) {
        return JsonSortUtil.jsonObjConverSortStr(data);
    }

    /**
     * BASE64字符串转二进制数据
     *
     * @param base64
     * @return
     * @throws Exception
     */
    private static byte[] decode(String base64) throws Exception {
        return new BASE64Decoder().decodeBuffer(base64);
    }

    /**
     * 二进制数据转BASE64字符串
     *
     * @param bytes
     * @return
     * @throws Exception
     */
    private static String encode(byte[] bytes) {
        return new String(new BASE64Encoder().encode(bytes));
    }

    /**
     * 测试
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        //baseTest();
        signDemo();
    }

    /**
     * 基本测试
     *
     * @throws Exception
     */
    private static void baseTest() throws Exception {
        Map<String, Object> keyPair = genKeyPair();
        // 公钥
        String publicKey = getPublicKey(keyPair);
        System.out.println("公钥-------------" + publicKey);

        // 私钥
        String privateKey = getPrivateKey(keyPair);
        System.out.println("私钥-------------" + privateKey);

        String source = "这是一行没有任何意义的文字，你看完了等于没看，不是吗？";

        // 签名
        String sign = RSAUtil.sign(source, privateKey);
        System.out.println("签名-------------" + sign);

        // 验签
        boolean status = RSAUtil.verify(source, publicKey, sign);
        System.out.println("验证结果-------------" + status);

    }

    /**
     * 验签完整demo
     *
     * @throws Exception
     */
    private static void signDemo() throws Exception {
        Map<String, Object> keyPair = genKeyPair();
        // 公钥
        String publicKey = getPublicKey(keyPair);
        System.out.println("公钥-------------" + publicKey);

        // 私钥
        String privateKey = getPrivateKey(keyPair);
        System.out.println("私钥-------------" + privateKey);

        String privateKeyOne = "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";
        String publicKeyOne = "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlWoBozb5kI3VqoD3vtNqzp2UQVcR0lzLI4DreTMagSHlfNx2VnAWHLU/LxDLSPOnPeH1kuPqStiIE+JVxefD21GMBgNJTSBk1scagQDnmpt2AF70GwJ9E1j97srqqrwxq47G9fQb1V2ofu10juMM73ymOKaJFd2CSg0rdjtga7pqlrO2tSQ4cPK5L9eVFZu16nxAKmVVpsJVhZGlpffoCgM59mynIi6a99U5Cb9PpE+Mk454pgIBI9xY+w2ccI+gQIibjzOMgke/xRVUxQwYc6XG9i+GspzXedcAnP2/NJ3UzXrcZAZ40k0cfiLzFkEAoZdXnNkuBrrbYn8Lh6D1uq89yOzbbu8mSqPUpelcZPr1iY7KGe2vEJvGToEMuWkNlfwem9YCc7Joesg0CCNA44AnxYK2s5VOPIgLXO3Af5wS3BMHiJ8USpxIWIxljBkQ2DdYnp4tkM4rrVi48/0mlhsNtL97pbgHR5JZTUVzeWpIRwGAHsNtiexboC8Hz/OgGvdJL7XLLLWy6LWwjRKI/bDAHI3gkJJQ0Ide/dcLSMMoIgEHVlpvmJ/WuGEENHRy38AY4Z3wONsMvcEjQaAw0uHi56jy6vS4QSBGqWgFgLwEH/DO/v6xTxcysMFRFIS51fA0ghK1rrCd3rfVsKkNwCu7O/l+bP8kOZ/wOs+AQGMCAwEAAQ==";
        Map<String, Object> params = new HashMap<>(4);
        params.put("productNo", "youxinqianbao");
        params.put("timestamp", "1622194445189");

        JSONObject jsonObject=new JSONObject();
        jsonObject.put("collidParameter","0E58FEC546B03DA75EE499FB247E9E27");
        jsonObject.put("collidType",3);
        params.put("data", jsonObject.toJSONString());

        // 排序并拼接成字符串
        String paramsStr = getSortParams(params);
        System.out.println("排序并拼接成字符串: " + paramsStr);

        // 签名
        System.out.println("签名-------------begin=");
        String sign = RSAUtil.sign(paramsStr, privateKeyOne);
        System.out.println("签名-------------" + sign);
        System.out.println("签名-------------end");

        params.put("sign", sign);

        System.out.println("post请求参数------------" + JSON.toJSONString(params));

        // 开始验签-----------------------------------------

        Map<String, Object> params2 = JSON.parseObject(JSON.toJSONString(params), Map.class);

        // 验签需要去除sign
        params2.remove("sign");

        // 排序并拼接成字符串
        String paramsStr2 = getSortParams(params2);

        // 验签
        boolean status = RSAUtil.verify(paramsStr2, publicKeyOne, sign);
        System.out.println("验证结果-------------" + status);
    }
}
